library(tidyr)
library(dplyr)
library(ggplot2)
library(data.table)
library(lubridate)
Goodreads è un social network dedicato ai libri creato nel dicembre 2006 dallo sviluppatore di software e imprenditore statunitense Otis Chandler.
Gli utenti, dopo la registrazione gratuita, possono aggiungere ai loro profili libri letti o da leggere, condividendo recensioni, commenti, votazioni, dati sull’acquisto e sulla lettura, suggerimenti con altri utenti, creare gruppi e partecipare a discussioni.
Il dataset preso in considerazione è tratto da kaggle.com` ed è composto da 20 colonne:
Per sistemare il dataset:
# Leggere il csv
goodreads <- read.csv("good_reads_final.csv")
df <- goodreads
# Cambiare codifica del file
fwrite(df,"good.csv")
goodreads<- fread("good.csv",encoding="UTF-8")
# Modifiche elencate sopra
goodreads<-goodreads %>%
mutate(author_genres = stringr::str_replace(author_genres,",","/")) %>%
separate(author_genres,into=c("first_genre","other_genre"),sep="/") %>%
mutate(other_genre = ifelse(is.na(other_genre) | other_genre=="", "unknown",other_genre)) %>%
mutate(other_genre = stringr::str_replace_all(other_genre,",","")) %>%
mutate(pages = as.integer(pages)) %>%
mutate(author_name = stringr::str_replace_all(author_name, "\n","")) %>%
mutate(birthplace = stringr::str_replace_all(birthplace, "\n",""))%>%
mutate(book_title = stringr::str_replace_all(book_title, "\n","")) %>%
mutate(first_genre = stringr::str_replace_all(first_genre,"-"," "),
other_genre = stringr::str_replace_all(other_genre,"-"," ")) %>%
mutate(birthplace = stringr::str_replace_all(birthplace, " ","")) %>%
mutate(birthplace = ifelse(birthplace == "","unknown",birthplace)) %>%
separate(publish_date,into=c("other","year"),sep=-4) %>%
select(-other, -author_page_url,-book_fullurl) %>%
mutate(year = ifelse(year=="" | grepl("th",year),NA,year)) %>%
mutate(year = as.integer(year))
## Warning: si è prodotto un NA per coercizione
## Warning: si è prodotto un NA per coercizione
head(goodreads,n=10)
## author_average_rating author_gender first_genre
## 1 4.01 female historical fiction
## 2 4.15 male literature fiction
## 3 4.00 female romance
## 4 3.88 male fiction
## 5 4.10 female young adult
## 6 3.77 male horror
## 7 4.16 female romance
## 8 3.94 female nonfiction
## 9 3.78 female fantasy
## 10 4.08 female young adult
## other_genre author_id author_name author_rating_count
## 1 unknown 74489 Victoria Thompson 74399
## 2 mystery thrillers 706255 Stieg Larsson 3726435
## 3 unknown 5618190 Mimi Jean Pamfiloff 76496
## 4 memoir 37871 José Donoso 5522
## 5 fantasy 36122 Patricia C. Wrede 291013
## 6 unknown 58947 Steve Niles 47938
## 7 unknown 4833990 Jillian Dodd 110522
## 8 unknown 7956 Mary Roach 321197
## 9 unknown 155651 Nancy Baker 1019
## 10 unknown 274533 Simone Elkeles 481114
## author_review_count birthplace book_average_rating book_id
## 1 6268 United States 4.02 686717
## 2 142704 Sweden 4.13 2429135
## 3 7975 United States 3.99 27833684
## 4 489 Chile 4.14 382975
## 5 13453 United States 4.01 64207
## 6 3240 United States 3.80 831829
## 7 9451 unknown 3.95 34804503
## 8 29747 United States 3.84 5981308
## 9 104 Canada 3.77 266600
## 10 25166 United States 4.01 544424
## book_title genre_1
## 1 Murder on St. Mark's Place Mystery
## 2 The Girl with the Dragon Tattoo Fiction
## 3 Tailored for Trouble Romance
## 4 The Obscene Bird of Night Fiction
## 5 Sorcery & Cecelia: or The Enchanted Chocolate Pot Fantasy
## 6 30 Days of Night, Vol. 1 Sequential Art
## 7 Stalk Me Young Adult
## 8 Bonk: The Curious Coupling of Science and Sex Nonfiction
## 9 Kiss of the Vampire Paranormal
## 10 Leaving Paradise Young Adult
## genre_2 num_ratings num_reviews pages year score
## 1 Historical 5260 375 277 2000 3230
## 2 Mystery 2229163 65227 465 2005 3062
## 3 Contemporary 2151 391 354 2016 4585
## 4 Magical Realism 1844 173 438 1970 1533
## 5 Young Adult 17051 1890 326 1988 2105
## 6 Sequential Art 17122 561 104 2004 4372
## 7 Romance 11684 1107 327 2012 2396
## 8 Science 45963 4268 319 2008 2054
## 9 Horror 594 42 278 1993 1311
## 10 Romance 40093 2375 303 2007 1994
authors<-goodreads %>%
select(author_average_rating:birthplace, book_id) %>%
select(author_id, author_name,everything()) %>%
group_by(author_name, author_gender) %>%
summarise(ratings=sum(author_rating_count), reviews = sum(author_review_count),n_books=n()) %>%
arrange(-ratings, -reviews)
authors
## # A tibble: 12,156 x 5
## # Groups: author_name [12,156]
## author_name author_gender ratings reviews n_books
## <chr> <chr> <int> <int> <int>
## 1 J.K. Rowling female 126640336 3098497 6
## 2 Stephen King male 72795154 2655847 6
## 3 Suzanne Collins female 61288176 2223663 6
## 4 Stephenie Meyer male 57284674 1835702 6
## 5 J.R.R. Tolkien male 43074264 655372 6
## 6 Rick Riordan male 42373362 1701792 6
## 7 Dan Brown male 36774808 1009774 6
## 8 Cassandra Clare female 35621747 1988989 6
## 9 John Green male 34033693 1938303 6
## 10 William Shakespeare male 33669143 576948 6
## # ... with 12,146 more rows
most_rated_authors <- authors %>%
arrange(-ratings) %>%
head(n=10) %>%
gather("ratings","reviews", key="type",value="number")
ggplot(most_rated_authors, aes(x=reorder(author_name,-number)))+
geom_bar(stat="identity", position="dodge",aes(y=number/1000000,fill=type)) +
labs(x = "Nome dell'autore", y="Numero di rating", title="Numero di valutazioni e recensioni (in milioni)")+
geom_text(aes(label=author_name), stat="count", size=3,angle=90, vjust=-0.5, hjust=-0.2)+
theme(axis.text.x=element_blank())
library(modelr)
rew_rat<- lm(reviews~ratings,authors)
authors %>% add_predictions(rew_rat) %>%
ggplot(aes(ratings))+
geom_jitter(aes(y=reviews), alpha = 0.5)+
geom_line(aes(y=pred), color="red3", size=1.6)
Dall’analisi risultano esserci più recensioni laddove le valutazioni sono in numero minore. Vi sono delle eccezioni, che probabilmente sono quelle precedentemente analizzate. Provando ad applicare una scala logaritmica ai dati, si nota che il modello si adatta meglio rispetto alla relazione lineare precedentemente evidenziata.
authors %>% add_predictions(rew_rat) %>%
ggplot(aes(ratings))+
geom_jitter(aes(y=reviews), alpha=0.5)+
geom_line(aes(y=pred), color="red3", size=1.6)+
scale_y_log10(limits=c(1,NA))
Alcune recensioni vanno in negativo perché molti libri hanno meno di 10 recensioni. Proviamo a indagare sulla distribuzione dei dati delle reviews:
summary(authors$reviews)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0 303 1291 17644 6427 3098497
I dati sono concentrati tra 303 e 17644, mentre il massimo supera i 3 milioni. Ci sono però anche libri che non hanno recensioni.
Proviamo a rappresentare anche i residui:
authors %>% add_predictions(rew_rat) %>%
add_residuals(rew_rat) %>%
ggplot(aes(ratings))+
geom_ref_line(h=0)+
geom_point(aes(y=resid))
Dal grafico dei residui emerge che i dati sono concentrati laddove i ratings e le recensioni sono minoritarie. Inoltre un punto in particolare si discosta dagli altri.
summary(rew_rat)
##
## Call:
## lm(formula = reviews ~ ratings, data = authors)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1287503 -6308 -5782 -3239 986714
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.441e+03 3.275e+02 19.67 <2e-16 ***
## ratings 3.458e-02 1.476e-04 234.36 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 35720 on 12154 degrees of freedom
## Multiple R-squared: 0.8188, Adjusted R-squared: 0.8188
## F-statistic: 5.493e+04 on 1 and 12154 DF, p-value: < 2.2e-16
Secondo il modello, la bontà del modello si aggira attorno all’82%.
rating<- authors %>%
select(author_name, reviews) %>%
left_join(select(goodreads, author_name, author_average_rating)) %>%
unique()
## Joining, by = "author_name"
rating
## # A tibble: 12,277 x 3
## # Groups: author_name [12,156]
## author_name reviews author_average_rating
## <chr> <int> <dbl>
## 1 J.K. Rowling 3098497 4.45
## 2 Stephen King 2655847 4.03
## 3 Suzanne Collins 2223663 4.26
## 4 Stephenie Meyer 1835702 3.64
## 5 J.R.R. Tolkien 655372 4.32
## 6 Rick Riordan 1701792 4.32
## 7 Dan Brown 1009774 3.81
## 8 Cassandra Clare 1988989 4.26
## 9 John Green 1938303 4.07
## 10 William Shakespeare 576948 3.86
## # ... with 12,267 more rows
ggplot(rating, aes(y=reviews,x=author_average_rating ))+
geom_jitter(aes(color=reviews))
summary(rating$author_average_rating)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.820 3.790 3.950 3.948 4.110 5.000
Come si nota dal grafico, le valutazioni sono elevate, infatti la media è su 4. C’è da notare però che nonostante le valutazioni siano alte, il numero di review è molto basso, quindi chi tende a valutare il libro generalmente non lo recensisce.
authors %>%
arrange(-reviews) %>%
ggplot(aes(x = n_books, y=reviews))+
geom_bar(stat="identity", aes(fill=reviews))+
scale_x_discrete(breaks = c(seq(1,6)))+
labs(x="Numero di libri scritti", y="Valutazioni")
Sembra che all’aumentare dei libri scritti aumentino anche le valutazioni. Probabilmente perchè gli scrittori sono incentivati a scrivere se ricevono feedback positivi dei precedenti capitoli. Questo ovviamente solo nel caso in cui siano famosi. Come emerge dal grafico ci sono comunque molti autori che pur avendo scritto molti libri non hanno eccellenti valutazioni rispetto ad altri.
Da notare però che se gli autori che scrivono molti libri sono tra i più recensiti, sono anche una cerchia ristretta, perché la maggioranza degli scrittori si limita a scrivere 1 o 2 libri (media e mediana).
summary(as.integer(authors$n_books))
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.000 1.000 1.000 1.883 2.000 6.000
authors$n_books = as.factor(authors$n_books)
authors %>%
arrange(-reviews) %>%
ggplot(aes(x = n_books))+
geom_bar(aes(fill = reviews), stat="count", fill="blue3")+
scale_x_discrete(breaks = c(seq(1,6)))+
labs(x="Numero di libri scritti", y="Scrittori")+
geom_text(aes(label=..count..),stat="count",vjust=-0.2)
ggplot(authors, aes(n_books,reviews))+
geom_boxplot(outlier.color = "blue", outlier.shape = NA)+
coord_flip()+
scale_y_discrete(breaks=seq(1,10,by=2))
Dal boxplot emerge comunque che la mediana aumenta a ogni libro in più scritto. In particolar modo la distribuzione dei dati con 6 libri è più variabile e contiene svariati outlier.
library(gridExtra)
par(mfrow = c(2,1))
genres1 <- goodreads %>%
count(genre_1) %>%
rename(genre = genre_1)
genres2 <- goodreads %>%
count(genre_2) %>%
rename(genre = genre_2)
genres <- full_join(genres1,genres2, by="genre") %>%
mutate(n.x = ifelse(is.na(n.x),0,n.x),
n.y = ifelse(is.na(n.y),0,n.y))%>%
group_by(genre) %>%
summarise(n = n.x+n.y)
loved_genres<- genres %>%
arrange(-n) %>%
head(10)
hollow_genres <- genres %>%
arrange(n) %>%
head(10)
loved_genres2<- loved_genres %>%
ggplot(aes(x=reorder(genre,n),y=n,fill=reorder(genre,n)))+
geom_bar(stat="identity", show.legend = TRUE)+
coord_polar(theta="y")+
theme_void()+
labs(title="Generi più famosi",
fill="Genere")
hollow_genres2 <- hollow_genres %>%
ggplot(aes(x=reorder(genre,n),y=n,fill=reorder(genre,n)))+
geom_bar(stat="identity", show.legend = TRUE)+
coord_polar(theta="y")+
theme_void()+
labs(title="Generi di nicchia",
fill="Genere")
grid.arrange(loved_genres2,hollow_genres2,ncol=2)
Dai due grafici a ciambella emergono i generi più letti e quelli più di nicchia, ovvero i cui testi sono rari. Per un’analisi più approfondita: Shiny App
Analizziamo ora il sesso degli scrittori, per analizzare se vi sono più scrittrici o scrittori e se c’è una relazione tra genere e successo.
authors %>%
group_by(author_gender) %>%
summarise(n=n())
## # A tibble: 2 x 2
## author_gender n
## <chr> <int>
## 1 female 5439
## 2 male 6717
Da una prima analisi emerge che vi sono più scrittori maschi che femmine. Analizziamo ora la relazione tra numero di libri scritti, reviews e genere dello scrittore.
authors %>%
ggplot(aes(x = n_books))+
geom_bar(stat="count",position="dodge", aes(fill=author_gender))+
labs(x="Numero di libri scritti", y="Scrittori")+
geom_text(aes(label=..count..), stat="count",
position=position_dodge(width=1))
Non sembra esserci una differenza sostanziale, ma proviamo a esplorare la relazione tra numero di reviews e sesso.
ggplot(authors, aes(x=author_gender, y=reviews, color=author_gender))+
geom_boxplot(outlier.size=2)+
scale_y_log10()
summary(authors$reviews)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0 303 1291 17644 6427 3098497
La distribuzione dei dati delle scrittrici sembra essere leggermente superiore rispetto a quella maschile, anche se la mediana sembra coincidere. Gli outlier superiori, quindi gli scrittori di successo, non sembrano differire di numero, mentre quelli inferiori sembrano essere il doppio rispetto agli scrittori maschi.
#Sopra il quantile 75%
authors[authors$reviews > quantile(authors$reviews,0.75) | authors$reviews < quantile(authors$reviews,0.25) ,] %>%
transform(quantile = ifelse(reviews > quantile(authors$reviews,0.75),0.75,0.25)) %>%
group_by(author_gender, quantile) %>%
summarise(n=n())
## # A tibble: 4 x 3
## # Groups: author_gender [2]
## author_gender quantile n
## <chr> <dbl> <int>
## 1 female 0.25 1222
## 2 female 0.75 1447
## 3 male 0.25 1814
## 4 male 0.75 1592
La maggioranza dei valori al di fuori del box per i maschi si trova al di sotto del quantile 0.25, mentre per le femmine si trova sopra il quantile 0.75.
Proviamo ad analizzare le frequenze relative:
n_writers <- nrow(authors)
authors %>%
ggplot(aes(x = n_books))+
geom_bar(position="dodge", aes(y=..count..,fill=author_gender))+
labs(x="Numero di libri scritti", y="Scrittori")
In termini di frequenze relative si può concludere che il numero di valutazioni.
goodreads %>%
select(birthplace) %>%
count(birthplace) %>%
arrange(-n) %>%
head(30)
## # A tibble: 30 x 2
## birthplace n
## <chr> <int>
## 1 " United States" 11471
## 2 unknown 4414
## 3 " United Kingdom" 2164
## 4 Canada 646
## 5 Japan 428
## 6 Australia 420
## 7 Germany 263
## 8 Egypt 250
## 9 India 230
## 10 Ireland 207
## # ... with 20 more rows
Di molti scrittori non è nota la nazionalità, ma la maggioranza proviene dagli stati uniti. Sembra comunque prevalere la lingua inglese sulle altre, probabilmente perchè l’applicazione è ideata da uno statunitense e pensata per essere monolingua. Tralasciando le nazionalità sconosciute:
top30countries <- goodreads %>%
select(birthplace, author_gender) %>%
count(birthplace, author_gender) %>%
filter(birthplace !="unknown") %>%
arrange(-n) %>%
head(30)
ggplot(top30countries,aes(birthplace,n))+
geom_bar(stat="identity", position="dodge", aes(fill=author_gender))+
geom_segment(aes(x=birthplace,
xend=birthplace,
y=min(n),
yend=max(n)),
linetype="dashed",
size=0.05,
color="grey")+
coord_flip()+
theme_classic()
library(DT)
book_list <- goodreads %>%
select(book_title,author_name, book_average_rating,genre_1,genre_2, pages, num_ratings) %>%
rename(rating = book_average_rating) %>%
arrange(-rating) %>%
unique()
DT::datatable(book_list)
Analizziamo però la relazione tra i rating e il numero di recensioni: se un libro ha poche recensioni, ma positive, il suo rating rimane alto, pur essendo letto da pochi. Se un libro ha molte recensioni, tutte variabili, nonostante abbia successo potrebbe avere dei ratings bassi.
ggplot(goodreads,aes(book_average_rating))+
geom_histogram(fill="lightblue")+
geom_freqpoly(color="blue")
Dal grafico emerge un’asimmetria negativa, che tende a sopravvalutare il libro, svalutando il ratings degli altri. Sembra esserci però una sorta di simmetria attorno al 4.
rating_cor<- goodreads %>%
select(author_average_rating,book_average_rating) %>%
mutate(author_average_rating = round(author_average_rating,0),
book_average_rating = round(book_average_rating,0)) %>%
rename(autore = author_average_rating,libro = book_average_rating)
library(corrplot)
corrplot(cor(rating_cor),method="color")
Sembra esserci una correlazione tra la valutazione degli scrittori e dei libri, una relazione crescente. Quindi al crescere del rating del libro, aumenta il rating del suo autore.
goodreads %>%
group_by(year) %>%
summarise(n_books = n()) %>%
ggplot(aes(year,n_books))+
geom_line()+
scale_x_continuous()
summary(goodreads$year)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## -720 2000 2011 1990 2013 2019 744
Verso gli anni 2000 c’è stata una crescita esponenziale del numero di libri pubblicati.
top_books<-goodreads %>%
select(book_id,book_title,num_ratings) %>%
group_by(book_title, book_id) %>%
summarise(ratings = sum(num_ratings))%>%
arrange(-ratings) %>%
left_join(select(goodreads,author_name,book_title)) %>%
unique() %>%
head(n=20)
## Joining, by = "book_title"
DT::datatable(top_books)
library(plotly)
top_books_plot<-top_books %>%
left_join(select(authors,author_name,author_gender), by="author_name") %>%
ggplot(aes(x=book_title, y=ratings, text=author_name, color=author_gender)) +
geom_point(size=3) +
geom_segment(aes(x=book_title,
xend=book_title,
y=0,
yend=ratings)) +
labs(title="Top books")+
theme()+
coord_flip()
ggplotly(top_books_plot, tooltip = "text")